package cn.base.web.support;

import cn.rengy.lang.BusinessException;
import cn.rengy.lang.CustomException;
import cn.rengy.lang.ResultEntity;
import cn.rengy.lang.WeChatException;
import cn.rengy.util.ExceptionPrintUtils;
import cn.rengy.util.servlet.ServletUtil;
import io.jsonwebtoken.ExpiredJwtException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.ui.Model;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.multipart.MaxUploadSizeExceededException;
import org.springframework.web.servlet.ModelAndView;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.NoHandlerFoundException;

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;

/**
 * Controller不使用try chath处理异常，在这里统一处理
 * 可以处理Controller捕获抛出的异常，不是Controller抛出的异常不能捕获
 * 其他异常由默认实现BasicErrorController处理
 * 不能处理404错误 因为DispatcherServlet的throwExceptionIfNoHandlerFound默认为false不抛出异常
 *
 * 如果需要处理404，需要设置spring.mvc.throw-exception-if-no-handler-found: true,
 * 和  spring.web.resources.add-mappings= false
 * 设置后者是因为DispatcherServlet的1057行
 * @author rengy
 * 参考默认实现 ResponseEntityExceptionHandler
 */
@ControllerAdvice()
public class AcmeControllerExceptionHandler {//extends ResponseEntityExceptionHandler

	private static Logger logger = LoggerFactory.getLogger(AcmeControllerExceptionHandler.class);
	/**
     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
     * @param binder
     */
    @InitBinder
    public void initBinder(HttpServletRequest request,WebDataBinder binder) {
    	//Object target=binder.getTarget();
    	//if(target instanceof WeChatVO) {
    	//	String appid=request.getHeader("Appid");
    	//	((WeChatVO)target).setAppid(appid);
    	//}
    }

    /**
     * 把值绑定到Model中，使全局@RequestMapping可以获取到该值
     * @param model
     * @throws Exception 
     */
    @ModelAttribute
    public void addAttributes(HttpServletRequest request,Model model) throws Exception {
    	//改成设置js全局变量domain
    }
    
	@ExceptionHandler({BusinessException.class,CustomException.class})
    Object handlerBusinessException(HttpServletRequest request, CustomException ex) {
		return handleExceptionInternal(request,ex.getErrorCode(),ex.getMessage());
    }

	/**
	 * 415 Unsupported Media Type 不支持的MediaType
	 * @param request
	 * @param e
	 * @return
	 */
	@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
	Object handleNoHandlerFoundException(HttpServletRequest request, HttpMediaTypeNotSupportedException e) {
		//MediaType错误不能判断是ajax或者网页请求，直接返回
		//直接返回 错误的httpstatus
		return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
				.body(ResultEntity.fail(HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(), "不支持的MediaType"))
				;
		//返回httpstatus200，错误码是415
		//return handleExceptionInternal(request, HttpStatus.UNSUPPORTED_MEDIA_TYPE.value(),"不支持的MediaType");
	}

	/**
	 * 404错误
	 */
	@ExceptionHandler(NoHandlerFoundException.class)
	Object handleNoHandlerFoundException(HttpServletRequest request, NoHandlerFoundException e) {
		//直接返回 错误的httpstatus
		return ResponseEntity.status(HttpStatus.NOT_FOUND)
				.body(ResultEntity.fail(HttpStatus.NOT_FOUND.value(), "您访问的地址不存在"))
				;
		//返回httpstatus200，错误码是404
		//return handleExceptionInternal(request, HttpStatus.NOT_FOUND.value(),"您访问的地址不存在");
	}
	 /**
     * 参数校验异常
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    Object handleMethodArgumentNotValidException(HttpServletRequest request,MethodArgumentNotValidException e) {
        // 按需重新封装需要返回的错误信息 解析原错误信息，封装后返回，此处返回非法的字段名称error.getField()，原始值error.getRejectedValue()，错误信息
        StringJoiner sj = new StringJoiner(";");
        e.getBindingResult().getFieldErrors().forEach(x -> sj.add(x.getDefaultMessage()));
        return handleExceptionInternal(request,1,sj.toString());
    }

	/**
	 * 不支持的method
	 * @param request
	 * @param e
	 * @return
	 */
	@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
	Object handleHttpRequestMethodNotSupportedException(HttpServletRequest request,HttpRequestMethodNotSupportedException e) {
		return handleExceptionInternal(request,HttpStatus.METHOD_NOT_ALLOWED,"不支持的method");
	}
    
	
	@ExceptionHandler(InvocationTargetException.class)
	Object handleUnknowException(HttpServletRequest request, InvocationTargetException ex) {
		Exception target=(Exception) ex.getTargetException();
		return handleRuntimeException( request, target);
	}
	
	@ExceptionHandler({
		ExpiredJwtException.class,
		MaxUploadSizeExceededException.class,
		WeChatException.class
	})
    Object handleRuntimeException(HttpServletRequest request, Exception ex) {
		if(ex instanceof CustomException){
			return handleExceptionInternal(request,((CustomException)ex).getErrorCode(),ex.getMessage());
		}else if (ex instanceof MaxUploadSizeExceededException) {
			return handleExceptionInternal(request,500,"上传文件过大");
		}else if (ex instanceof WeChatException){
			WeChatException e=(WeChatException)ex;
			logger.error("微信接口异常{}:{}",e.getErrorCode(),e.getMessage());
			return handleExceptionInternal(request,500,ex.getMessage());
		}else if (ex instanceof HttpRequestMethodNotSupportedException) {
			return handleExceptionInternal(request,401,ex.getMessage());
		}else {
	        return handleUnknowException(request,ex);
		}
    }
	
	
	
	/**
	 * 未知异常保存数据库
	 * @param request
	 * @param ex
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	Object handleUnknowException(HttpServletRequest request, Exception ex) {
		logger.error(ExceptionPrintUtils.getTrace(ex));
		//dbService.update("insert_sys_error", ex.getClass().getName(),2,ExceptionUtil.stacktraceToString(ex,10000));
		String message="error";
        return handleExceptionInternal(request,1,ex.getMessage());
        
    }
	
	protected Object handleExceptionInternal(HttpServletRequest request,Object errcode,String message) {
		//1.若为AJAX请求 返回JSON
        if (ServletUtil.isAjaxRequest(request)) {
        	return ResponseEntity.ok(ResultEntity.fail(errcode, message));
        }
        //2.链接请求 返回View
        Map<String, Object> model=new HashMap<String,Object>(1,1);
        model.put("message", message);
		model.put("status", errcode);
        return new ModelAndView("error",model);
	}
}
